﻿using System;

namespace Pfz
{
	/// <summary>
	/// Class that guarantees that its value will be disposed, even if you forget to call dispose.
	/// </summary>
	public sealed class DisposeAssurer:
		DisposeAssurerBase
	{
		private DisposeAssurer()
		{
		}

		/// <summary>
		/// Creates a new DisposeAssurer for the given IDisposable value.
		/// In case of exception, the value is disposed.
		/// </summary>
		public static DisposeAssurer Create(IDisposable value)
		{
			if (value == null)
				throw new ArgumentNullException("value");

			try
			{
				var result = new DisposeAssurer();
				lock(_lock)
				{
					int referenceCount;
					_dictionary.TryGetValue(value, out referenceCount);
					referenceCount++;
					_dictionary[value] = referenceCount;
				}

				result._value = value;
				return result;
			}
			catch
			{
				value.Dispose();
				throw;
			}
		}

		/// <summary>
		/// Creates a rightly typed DisposeAssurer for the given value.
		/// In case of exception, the value is disposed.
		/// </summary>
		public static DisposeAssurer<T> Create<T>(T value)
		where
			T: class, IDisposable
		{
			if (value == null)
				throw new ArgumentNullException("value");

			try
			{
				var result = new DisposeAssurer<T>();
				lock(_lock)
				{
					int referenceCount;
					_dictionary.TryGetValue(value, out referenceCount);
					_dictionary[value] = referenceCount;
				}

				result._value = value;
				return result;
			}
			catch
			{
				value.Dispose();
				throw;
			}
		}

		/// <summary>
		/// Gets the value of this instance.
		/// </summary>
		public new IDisposable Value
		{
			get
			{
				return base.Value;
			}
		}
	}

	/// <summary>
	/// Class that guarantees that it value will be disposed, even if you forget to call dispose.
	/// </summary>
	public sealed class DisposeAssurer<T>:
		DisposeAssurerBase<T>
	where
		T: class, IDisposable
	{
		internal DisposeAssurer()
		{
		}

		/// <summary>
		/// Gets the value of this instance.
		/// </summary>
		public new T Value
		{
			get
			{
				return base.Value;
			}
		}
	}
}
